package com.ruicar.afs.cloud.auth.endpoint;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruicar.afs.cloud.auth.oauth2.AfsSerializationRedisSerializer;
import com.ruicar.afs.cloud.common.core.constant.CommonConstants;
import com.ruicar.afs.cloud.common.core.constant.PaginationConstants;
import com.ruicar.afs.cloud.common.core.constant.SecurityConstants;
import com.ruicar.afs.cloud.common.core.util.IResponse;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.ruicar.afs.cloud.auth.constant.Constants.TOKEN_CACHE_PREFIX;

/**
 * @author Fzero
 * @date 2019-01-22
 * 删除token端点
 */
@RestController
@AllArgsConstructor
@RequestMapping("/token")
public class AfsTokenEndpoint {
	private static final String _OAUTH_ACCESS = SecurityConstants._PREFIX + SecurityConstants.OAUTH_PREFIX + "auth_to_access*";
	private final TokenStore tokenStore;
	private final RedisTemplate redisTemplate;
	private final CacheManager cacheManager;
	private final StringRedisTemplate stringRedisTemplate;
	/**
	 * 退出token
	 *
	 * @param authHeader Authorization
	 */
	@DeleteMapping("/logout")
	public IResponse logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) {
		if (StrUtil.isBlank(authHeader)) {
			return IResponse.success("退出成功");
		}

		String tokenValue = authHeader.replace("Bearer", "").trim();
		OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
		if (accessToken == null || StrUtil.isBlank(accessToken.getValue())) {
			return IResponse.success("退出成功");
		}

		OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(accessToken);
		cacheManager.getCache(CommonConstants.USER_CACHE_KEY_PREFIX).evict(auth2Authentication.getName());
		tokenStore.removeAccessToken(accessToken);
		removeTokenCache(auth2Authentication.getName(),accessToken.getValue());
		return new IResponse<>(Boolean.TRUE);
	}

	private void removeTokenCache(String userName,String oldToken){
		List<String> tokens = new ArrayList<>();
		String token=stringRedisTemplate.opsForValue().get(TOKEN_CACHE_PREFIX+userName);
		if(StringUtils.isNoneEmpty(token)){
			tokens.addAll(Arrays.asList(token.split(",")));
		}
		tokens = tokens.stream().filter(str-> !StringUtils.equals(str,oldToken)).collect(Collectors.toList());
		if(!CollectionUtil.isEmpty(tokens)){
			stringRedisTemplate.opsForValue().set(TOKEN_CACHE_PREFIX+userName, CollectionUtil.join(tokens,","),2L, TimeUnit.HOURS);
		}else {
			stringRedisTemplate.delete(TOKEN_CACHE_PREFIX+userName);
		}

	}
	/**
	 * 令牌管理调用
	 *
	 * @param token token
	 * @return
	 */
	@DeleteMapping("/{token}")
	public IResponse<Boolean> delToken(@PathVariable("token") String token) {
		OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
		if(oAuth2AccessToken!=null){
			OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(oAuth2AccessToken);
			removeTokenCache(auth2Authentication.getName(), token);
			tokenStore.removeAccessToken(oAuth2AccessToken);
		}
		return new IResponse<>();
	}


	/**
	 * 查询token
	 *
	 * @param params 分页参数
	 * @return
	 */
	@PostMapping("/page")
	public IResponse<Page> tokenList(@RequestBody Map<String, Object> params) {
		//根据分页参数获取对应数据
		List<String> pages = findKeysForPage(_OAUTH_ACCESS, MapUtil.getInt(params, PaginationConstants.CURRENT)
				, MapUtil.getInt(params, PaginationConstants.SIZE));

		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new AfsSerializationRedisSerializer());
		Page result = new Page(MapUtil.getInt(params, PaginationConstants.CURRENT), MapUtil.getInt(params, PaginationConstants.SIZE));
		result.setRecords(redisTemplate.opsForValue().multiGet(pages));
		result.setTotal(Long.valueOf(redisTemplate.keys(_OAUTH_ACCESS).size()));
		return new IResponse<>(result);
	}


	private List<String> findKeysForPage(String patternKey, int pageNum, int pageSize) {
		ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
		RedisSerializer<String> redisSerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
		Cursor cursor = (Cursor) redisTemplate.executeWithStickyConnection(redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
		List<String> result = new ArrayList<>();
		int tmpIndex = 0;
		int startIndex = (pageNum - 1) * pageSize;
		int end = pageNum * pageSize;

		assert cursor != null;
		while (cursor.hasNext()) {
			if (tmpIndex >= startIndex && tmpIndex < end) {
				result.add(cursor.next().toString());
				tmpIndex++;
				continue;
			}
			if (tmpIndex >= end) {
				break;
			}
			tmpIndex++;
			cursor.next();
		}
		return result;
	}

	@PostMapping("/kickOut/{userName}")
	public IResponse<Boolean> kickOut(@PathVariable("userName") String userName) {
		if(!StringUtils.isEmpty(userName)){
			String token=stringRedisTemplate.opsForValue().get(TOKEN_CACHE_PREFIX+userName);
			List<String> tokens = new ArrayList<>();
			if(StringUtils.isNoneEmpty(token)){
				tokens.addAll(Arrays.asList(token.split(",")));
			}
			tokens.stream().forEach(strToken->{
				delToken(strToken);
			});
		}
		return new IResponse<>();
	}

}
